#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include "image_u32.h"

#define ALIGNMENT 16

image_u32_t *image_u32_create(int width, int height)
{
    image_u32_t *im = (image_u32_t*) calloc(1, sizeof(image_u32_t));

    im->width = width;
    im->height = height;
    im->stride = width;

    // XXX we should just align our buffers using posix_memalign?
    if ((im->stride % ALIGNMENT) != 0)
        im->stride += ALIGNMENT - (im->stride % ALIGNMENT);

    im->buf = (uint32_t*) calloc(im->height*im->stride, sizeof(uint32_t));

    return im;
}

void image_u32_destroy(image_u32_t *im)
{
    free(im->buf);
    free(im);
}


image_u32_t *image_u32_create_from_pnm(const char *path)
{
    int width, height, format;
    image_u32_t *im = NULL;
    uint8_t *buf = NULL;

    FILE *f = fopen(path, "rb");
    if (f == NULL)
        return NULL;

    if (3 != fscanf(f, "P%d\n%d %d\n255\n", &format, &width, &height))
        goto error;


    im = image_u32_create(width, height);

    // Binary Gray -- copy the gray channel into r,g,b spots
    if (format == 5)
    {
        int stride = width;
        int sz = stride*height;
        buf = malloc(sz);

        if (sz != fread(buf, 1, sz, f))
            goto error;

        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int gidx = y*stride +x;
                int ridx = y*im->stride +x;
                uint8_t v = buf[gidx];

                im->buf[ridx] = (v<<16) | (v<<8) | v;
            }
        }
        free(buf);

        return im;
    }
    // Binary RGB -- copy 3 bytes of color into 32 bytes
    if (format == 6) {
        int stride = width*3;
        int sz = stride*height;
        buf = malloc(sz);

        if (sz != fread(buf, 1, sz, f))
            goto error;

        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int gidx = y*stride + 3*x;
                int ridx = y*im->stride +x;
                uint8_t r = buf[gidx + 0];
                uint8_t g = buf[gidx + 1];
                uint8_t b = buf[gidx + 2];

                im->buf[ridx] = (r<<16) | (g<<8) | b;
            }
        }
        free(buf);

        return im;
    }

error:
    fclose(f);

    if (im != NULL)
        image_u32_destroy(im);

    if (buf != NULL)
        free(buf);

    return NULL;
}

int image_u32_write_pnm(const image_u32_t *im, const char *path)
{
    FILE *f = fopen(path, "wb");
    uint8_t * buf2 = NULL;

    int res = 0;

    if (f == NULL) {
        res = -1;
        goto finish;
    }

    fprintf(f, "P6\n%d %d\n255\n", im->width, im->height);

    // Do an in-memory copy first for efficiency
    uint32_t len2 = im->width*im->height*3;
    buf2 = (uint8_t*)malloc(len2);
    uint32_t stride2 = im->width*3;
    for (uint32_t y = 0; y < im->height; y++) {
        for (uint32_t x = 0; x < im->width; x++) {
            buf2[y*stride2 + 3*x + 0 ] = (im->buf[y*im->stride +x] >> 16) & 0xff;
            buf2[y*stride2 + 3*x + 1 ] = (im->buf[y*im->stride +x] >>  8) & 0xff;
            buf2[y*stride2 + 3*x + 2 ] = (im->buf[y*im->stride +x] >>  0) & 0xff;
        }
    }

    // Do one contiguous write operation
    if (len2 != fwrite(buf2, 1, len2, f)) {
        res = -2;
        goto finish;
    }


finish:
    if (f != NULL)
        fclose(f);

    if (buf2 != NULL)
        free(buf2);

    return res;
}


void image_u32_fill_from_f32(image_u32_t * t, const image_f32_t *a, const image_f32_t *b, const image_f32_t *c, const image_f32_t *d)
{

    assert(0);
    // TODO
}
